package cz.semjob.framework;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import cz.semjob.document.annotation.LabeledRelationWordsPair;
import cz.semjob.document.annotation.LabeledWords;
import cz.semjob.document.annotation.RelationWordsPair;
import cz.semjob.document.annotation.metadata.AnnotationTypeFactory;
import cz.semjob.document.annotation.metadata.Entity;
import cz.semjob.document.annotation.metadata.Relation;
import cz.semjob.document.processing.DocumentInstance;
import cz.semjob.document.processing.DocumentProcessingException;
import cz.semjob.document.processing.batch.DocumentsBatchProcessor;
import cz.semjob.learning.classifier.ClassifierExecutor;
import cz.semjob.learning.instances.creator.ITextInstanceCreator;
import cz.semjob.learning.relation.IRelationInstanceCreator;
import cz.semjob.output.data.ClassificationInfo;

public class DocumentAnnotator {

	@Autowired
	private DocumentsBatchProcessor documentBatchProcessor;
	@Autowired
	private ClassifierInstanceCreatorReader classifierInstanceCreatorReader;
	@Autowired
	private ClassifierExecutor classifierExecutor;
	@Autowired
	private ClassificatonRelationHelper classificatonRelationHelper;
	@Autowired
	private AnnotationTypeFactory annotationTypeFactory;
	private Logger logger = Logger.getLogger(DocumentAnnotator.class);
	
	private String conceptsClassifiersPath;
	
	private String relationsClassifiersPath;

	public List<DocumentAnnotationResults> annotateDocuments(
			String documentPath, String processedPath, String conceptsClassifiersPath, String relationsClassifiersPath)
			throws DocumentAnnotationException {
		
		if (conceptsClassifiersPath ==null) {
			conceptsClassifiersPath = this.conceptsClassifiersPath;
		}
		
		if (relationsClassifiersPath ==null) {
			relationsClassifiersPath = this.relationsClassifiersPath;
		}

		Map<DocumentInstance, DocumentAnnotationResults> documentAnnotationResultsMap = new HashMap<DocumentInstance, DocumentAnnotationResults>();
		List<DocumentInstance> documentInstances;

		try {
			documentInstances = documentBatchProcessor
					.processDocumentInstancesBatch(documentPath, processedPath);
		} catch (DocumentProcessingException e) {
			throw new DocumentAnnotationException(e);
		}

		for (DocumentInstance documentInstance : documentInstances) {
			documentAnnotationResultsMap.put(documentInstance,
					new DocumentAnnotationResults(documentInstance));
		}

		List<ClassifierInstanceCreatorPair> conceptClassifiers = classifierInstanceCreatorReader
				.readClassifiers(conceptsClassifiersPath, true);
		List<ClassifierInstanceCreatorPair> relationsClassifiers = classifierInstanceCreatorReader
				.readClassifiers(relationsClassifiersPath, false);
		Map<String, Map<DocumentInstance, List<LabeledWords>>> dataSources = new HashMap<String, Map<DocumentInstance, List<LabeledWords>>>();

		// TODO zaistit, aby mali data sources spravne poradie
		for (ClassifierInstanceCreatorPair conceptClassifier : conceptClassifiers) {
			try {
				ClassificationInfo classificationInfo = new ClassificationInfo(conceptClassifier.getPrecision(), conceptClassifier.getRecall(), conceptClassifier.getFmeasure(), documentInstances.size(), conceptClassifier.getClassifierInfo());
				ITextInstanceCreator textInstanceCreator = (ITextInstanceCreator) conceptClassifier
						.getInstanceCreator();
				Map<DocumentInstance, List<LabeledWords>> foundAnnotations = null;
				Entity entity = annotationTypeFactory
						.getEntity(conceptClassifier.getAnnotationType());
				if (textInstanceCreator.getDataSourcesNames().size() > 0) {
					List<Object> currentDataSources = new ArrayList<Object>();
					for (String dataSourceName : textInstanceCreator
							.getDataSourcesNames()) {
						currentDataSources.add(dataSources.get(dataSourceName));
					}
				
					foundAnnotations = classifierExecutor
							.executeClassificationForEntity(conceptClassifier
									.getClassifier(), entity,
									(ITextInstanceCreator) conceptClassifier
											.getInstanceCreator(),
									documentInstances, currentDataSources
											.toArray());
				} else {
					foundAnnotations = classifierExecutor
							.executeClassificationForEntity(conceptClassifier
									.getClassifier(), entity,
									(ITextInstanceCreator) conceptClassifier
											.getInstanceCreator(),
									documentInstances);
				}
				dataSources.put(
						conceptClassifier.getAnnotationType(),
						foundAnnotations);
				for (DocumentInstance documentInstance : foundAnnotations
						.keySet()) {
					List<LabeledWords> annotations = foundAnnotations
							.get(documentInstance);
					DocumentAnnotationResults documentAnnotationResults = documentAnnotationResultsMap.get(documentInstance);
					documentAnnotationResults
							.addConceptAnnotations(annotations);
					
					documentAnnotationResults.addClassifierInfo(entity.getName(), classificationInfo);
				}
			} catch (Exception e) {
				throw new DocumentAnnotationException(e);
			}
		}
		
		for (ClassifierInstanceCreatorPair relationClassifier : relationsClassifiers) {
			try {
				String relationType = relationClassifier.getAnnotationType();
				ClassificationInfo classificationInfo = new ClassificationInfo(relationClassifier.getPrecision(), relationClassifier.getRecall(), relationClassifier.getFmeasure(), documentInstances.size(), relationClassifier.getClassifierInfo());
				Map<DocumentInstance, List<RelationWordsPair>> candidateRelations = classificatonRelationHelper
						.createCandidateRelationInstances(
								documentAnnotationResultsMap, relationType);
				Map<DocumentInstance, Map<Relation, List<LabeledRelationWordsPair>>> foundAnnotations = classifierExecutor
						.executeClassificationForRelations(relationClassifier
								.getClassifier(), relationType,
								(IRelationInstanceCreator) relationClassifier
										.getInstanceCreator(),
								candidateRelations);
				for (DocumentInstance documentInstance : foundAnnotations
						.keySet()) {
					Map<Relation, List<LabeledRelationWordsPair>> annotations = foundAnnotations
							.get(documentInstance);
					for (List<LabeledRelationWordsPair> relationAnnotations : annotations
							.values()) {
						documentAnnotationResultsMap.get(documentInstance)
								.addRelationsAnnotations(relationAnnotations);
					}
					documentAnnotationResultsMap.get(documentInstance).addClassifierInfo(relationType, classificationInfo);
				}
			} catch (Exception e) {
				throw new DocumentAnnotationException(e);
			}
		}
		
		for (DocumentAnnotationResults results : documentAnnotationResultsMap.values()) {
			logger.info(results.printCounts());
		}

		return new ArrayList<DocumentAnnotationResults>(
				documentAnnotationResultsMap.values());
	}

	public void setDocumentBatchProcessor(
			DocumentsBatchProcessor documentBatchProcessor) {
		this.documentBatchProcessor = documentBatchProcessor;
	}

	public void setClassifierInstanceCreatorReader(
			ClassifierInstanceCreatorReader classifierInstanceCreatorReader) {
		this.classifierInstanceCreatorReader = classifierInstanceCreatorReader;
	}

	public void setClassifierExecutor(ClassifierExecutor classifierExecutor) {
		this.classifierExecutor = classifierExecutor;
	}

	public void setClassificatonRelationHelper(
			ClassificatonRelationHelper classificatonRelationHelper) {
		this.classificatonRelationHelper = classificatonRelationHelper;
	}

	public void setConceptsClassifiersPath(String conceptsClassifiersPath) {
		this.conceptsClassifiersPath = conceptsClassifiersPath;
	}

	public void setRelationsClassifiersPath(String relationsClassifiersPath) {
		this.relationsClassifiersPath = relationsClassifiersPath;
	}

}
